﻿using Gumini.JulaDB.Enums;
using Gumini.JulaDB.Provider;
using Gumini.JulaDB.Test.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Gumini.JulaDB.Test
{
    /// <summary>
    /// A set of tests for simple expressions in the SELECT statement that don't depend on source table content.
    /// </summary>
    [TestClass]
    public class ExpressionTest
    {
        private const double _epsilon = 0.0001;
        private static JulaDbConnection _connection;

        [ClassInitialize]
        public static void InitializeDatabase(TestContext context)
        {
            _connection = TestUtils.CreateTestDatabase();
        }

        [ClassCleanup]
        public static void CleanupDatabase()
        {
            _connection.Close();
        }

        /// <summary>
        /// A simple division of two numbers.
        /// </summary>
        [TestMethod]
        public void SimpleDivision()
        {
            var recordSet = _connection.ExecuteValidSelect("select 1 / 3 from dual");

            Assert.AreEqual(1, recordSet.Rows.Count);
            Assert.AreEqual(1, recordSet.Columns.Count);

            var row = recordSet.Rows[0];
            Assert.AreEqual(0.33333, (double)(decimal)row[0], _epsilon);
        }

        /// <summary>
        /// Simple arithmetic expressions. One has an alias and the other doesn't. The column name for the second expression should be autogenerated.
        /// </summary>
        [TestMethod]
        public void SimpleAliasedSelect()
        {
            var dataTable = _connection.ExecuteValidSelect("select 1 / 3 as OneThird, 2 + 4 * 2 from dual");

            Assert.AreEqual(1, dataTable.Rows.Count);
            
            dataTable.CheckColumnNames("OneThird", "column_0");
            
            var row = dataTable.Rows[0];
            Assert.AreEqual(0.33333, (double)(decimal)row[0], _epsilon);
            Assert.AreEqual(10M, (decimal)row[1]);
        }

        /// <summary>
        /// A string concatenation.
        /// </summary>
        [TestMethod]
        public void SimpleConcatenation()
        {
            var dataTable = _connection.ExecuteValidSelect("select 'Jan' || ' ' || 'Kowalski' as Name from dual");
            
            Assert.AreEqual(1, dataTable.Rows.Count);

            dataTable.CheckColumnNames("Name");

            var row = dataTable.Rows[0];
            Assert.AreEqual("Jan Kowalski", (string)row[0]);
        }

        /// <summary>
        /// A test of unary minur operator in an expression.
        /// </summary>
        [TestMethod]
        public void UnaryMinus()
        {
            var recordSet = _connection.ExecuteValidSelect("select 1 + (-2) from dual");
            
            var row = recordSet.Rows[0];
            Assert.AreEqual(-1M, (decimal)row[0]);
        }

        /// <summary>
        /// A test for implicit conversion of varchar to number. Number addition should be performed on both operands.
        /// </summary>
        [TestMethod]
        public void ImplicitConversionAddition()
        {
            _connection.ExecuteValidScalar("select 2 + '2' from dual", 4);
        }

        /// <summary>
        /// Implicit conversion of number to varchar. String concatenation should be performed.
        /// </summary>
        [TestMethod]
        public void ImplicitConversionConcatenation()
        {
            _connection.ExecuteValidScalar("select 2 || '2' from dual", "22");
        }

        /// <summary>
        /// Implicit conversion is not possible, as we cannot convert a letter to a number. This should fail with an appropriate error code.
        /// </summary>
        [TestMethod]
        public void ImplicitConversionError()
        {
            _connection.ExecuteInvalidCommand("select 2 + 'A' from dual", ErrorCode.TypeError);
        }

        /// <summary>
        /// A second test for the conversion error.
        /// </summary>
        [TestMethod]
        public void ImplicitConversionError2()
        {
            _connection.ExecuteInvalidCommand("select 2 * 'a' from dual", ErrorCode.TypeError);
        }

        /// <summary>
        /// Use of boolean literals true and false in expressions.
        /// </summary>
        [TestMethod]
        public void BooleanLiteral()
        {
            _connection.ExecuteValidScalar("select false from dual where true", false);
        }
    }
}
